# coding=utf-8
from phosphorusScripts.common.shared import COLOR_PATTERN, OptionType


class ConfigBuilder(object):

    def __init__(self):
        self._sequence = []
        """:type: list[str]"""
        self._content = {}
        """:type: dict[str, dict]"""
        self._operator = {}
        """:type: dict[str, bool]"""
        self._callback = {}
        """:type: dict[str, function]"""

    def switch(self, key, current, default):
        """
        添加开关选项。
        :param str key: 键名
        :param bool current: 当前值
        :param bool default: 默认值
        :rtype: ConfigBuilder
        """
        if type(key) is not str:
            raise TypeError('key must be str')
        if type(current) is not bool:
            raise TypeError('current must be bool')
        if type(default) is not bool:
            raise TypeError('default must be bool')

        self._append(key, {
            'type': OptionType.SWITCH,
            'current': current,
            'default': default,
        })
        return self

    def enum(self, key, current, default, valid):
        """
        添加枚举选项。
        :param str key: 键名
        :param str current: 当前值
        :param str default: 默认值
        :param tuple[str] valid: 允许值
        :rtype: ConfigBuilder
        """
        if type(key) is not str:
            raise TypeError('key must be str')
        if type(current) is not str:
            raise TypeError('current must be str')
        if type(default) is not str:
            raise TypeError('default must be str')
        if type(valid) is not tuple:
            raise TypeError('valid must be tuple[str]')
        if not all(type(i) is str for i in valid):
            raise TypeError('valid must be tuple[str]')

        if current not in valid:
            raise ValueError('current must be in valid')
        if default not in valid:
            raise ValueError('current must be in valid')

        self._append(key, {
            'type': OptionType.ENUM,
            'current': current,
            'default': default,
            'valid': valid,
        })
        return self

    def int(self, key, current, default, minimum=0, maximum=65535):
        """
        添加整型选项。
        :param str key: 键名
        :param int current: 当前值
        :param int default: 默认值
        :param int minimum: 最小值
        :param int maximum: 最大值
        :rtype: ConfigBuilder
        """
        if type(key) is not str:
            raise TypeError('key must be str')
        if type(current) is not int:
            raise TypeError('current must be int')
        if type(default) is not int:
            raise TypeError('default must be int')
        if type(minimum) is not int:
            raise TypeError('minimum must be int')
        if type(maximum) is not int:
            raise TypeError('maximum must be int')

        if current < minimum or current > maximum:
            raise ValueError('current must be between minimum and maximum')
        if default < minimum or default > maximum:
            raise ValueError('default must be between minimum and maximum')

        self._append(key, {
            'type': OptionType.INT,
            'current': current,
            'default': default,
            'min': minimum,
            'max': maximum,
        })
        return self

    def float(self, key, current, default, minimum=0.0, maximum=1.0):
        """
        添加浮点型选项。
        :param str key: 键名
        :param float current: 当前值
        :param float default: 默认值
        :param float minimum: 最小值
        :param float maximum: 最大值
        :rtype: ConfigBuilder
        """
        if type(key) is not str:
            raise TypeError('key must be str')
        if type(current) is not float:
            raise TypeError('current must be float')
        if type(default) is not float:
            raise TypeError('default must be float')
        if type(minimum) is not float:
            raise TypeError('minimum must be float')
        if type(maximum) is not float:
            raise TypeError('maximum must be float')

        if current < minimum or current > maximum:
            raise ValueError('current must be between minimum and maximum')
        if default < minimum or default > maximum:
            raise ValueError('default must be between minimum and maximum')

        self._append(key, {
            'type': OptionType.FLOAT,
            'current': current,
            'default': default,
            'min': minimum,
            'max': maximum,
        })
        return self

    def color(self, key, current, default):
        """
        添加颜色选项。
        :param str key: 键名
        :param str current: 当前值
        :param str default: 默认值
        :rtype: ConfigBuilder
        """
        if type(key) is not str:
            raise TypeError('key must be str')
        if type(current) is not str:
            raise TypeError('current must be str')
        if type(default) is not str:
            raise TypeError('default must be str')

        if not COLOR_PATTERN.match(current):
            raise ValueError('current must be a 6-digit hexadecimal code')
        if not COLOR_PATTERN.match(default):
            raise ValueError('default must be a 6-digit hexadecimal code')

        self._append(key, {
            'type': OptionType.COLOR,
            'current': current,
            'default': default,
        })
        return self

    def button(self, key):
        """
        添加按钮选项。
        :param str key: 键名
        :rtype: ConfigBuilder
        """
        if type(key) is not str:
            raise TypeError('key must be str')

        self._append(key, {
            'type': OptionType.BUTTON,
        })
        return self

    def operator(self):
        """
        给上一个添加的选项添加管理员权限限制。
        拥有此限制的选项，只能由管理员进行修改或交互。
        :rtype: ConfigBuilder
        """
        self._operator[self._sequence[-1]] = True
        return self

    def callback(self, func):
        """
        给上一个添加的选项添加回调函数。
        拥有回调函数的选项，在修改或交互后，仅调用回调函数，不再触发事件。
        :param function func: 回调函数
        :rtype: ConfigBuilder
        """
        if not callable(func):
            raise TypeError('func must be callable')

        self._callback[self._sequence[-1]] = func
        return self

    def build(self):
        """
        返回构建好的配置字典，可用于 comp.RegisterConfig(key, builtDict) 。
        :rtype: dict
        """
        return {
            'sequence': self._sequence,
            'content': self._content,
            'operator': self._operator,
            'callback': self._callback,
        }

    def _append(self, key, value):
        # 如果key相同，则移除旧的key
        if self._content.pop(key, None):
            self._sequence.remove(key)
        self._operator.pop(key, None)
        self._callback.pop(key, None)
        # 添加
        self._sequence.append(key)
        self._content[key] = value

    def select(self, key, current, default, valid):
        """
        已弃用，请使用 builder.enum(key, current, default, valid)。
        添加枚举选项。
        :param str key: 键名
        :param str current: 当前值
        :param str default: 默认值
        :param tuple[str] valid: 允许值
        :rtype: ConfigBuilder
        """
        return self.enum(key, current, default, valid)

    def needOp(self, isNeed=True):
        """
        已弃用，请使用 builder.operator() 。
        给上一个添加的选项添加管理员权限限制。
        :param bool isNeed: 已弃用
        :rtype: ConfigBuilder
        """
        return self.operator()
